/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "spatial_awareness_mgr_service.h"

#include <thread>
#include <unistd.h>
#include <ipc_skeleton.h>
#include "file_ex.h"
#include "communicate_factory.h"
#include "iservice_registry.h"
#include "system_ability_definition.h"
#include "spatial_awareness_common.h"
#include "spatial_master_epoch.h"
#include "spatial_slave_epoch.h"
#include "constants.h"
#include "spatial_semaphore.h"
#include "spatial_dump.h"

namespace OHOS {
namespace Msdp {
namespace {
auto swms = DelayedSpSingleton<SpatialaWarenessMgrService>::GetInstance();
const bool G_REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(swms.GetRefPtr());
const std::string SPATIAL_AWARENESS_SERVICE_NAME = "SpatialaWarenessMgrService";
sptr<ISystemAbilityManager> samgr_;
SpatialSemaphore sem(0);
constexpr int32_t MAX_DMUP_PARAM = 2;
}

SpatialaWarenessMgrService::SpatialaWarenessMgrService() : SystemAbility(MSDP_SPATIAL_AWARENESS_SERVICE_ID, true)
{
}

SpatialaWarenessMgrService::~SpatialaWarenessMgrService()
{
}

void SpatialaWarenessMgrService::OnStart()
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s: enter.", __func__);
    int sleepTime = 5000;
    std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime));
    if (!Init(false)) {
        SPATIAL_LOGE(MODULE_SERVICE,
            "%{public}s: OnStart call init fail", __func__);
        return;
    }
    if (!Publish(DelayedSpSingleton<SpatialaWarenessMgrService>::GetInstance())) {
        SPATIAL_LOGE(MODULE_SERVICE,
            "%{public}s: OnStart register to system ability manager failed.", __func__);
        return;
    }
    SPATIAL_LOGI(MODULE_SERVICE,
        "OnStart and add system ability success.");
}

void SpatialaWarenessMgrService::OnStop()
{
    algorithmHandle_->UninstallAllLib();
    communicator_->RemoveSessionServer();
}

bool SpatialaWarenessMgrService::Init(bool mock)
{
    SPATIAL_LOGI(MODULE_SERVICE, "SpatialaWarenessMgrService Init enter!");
    if (!InitEventHandler()) {
        SPATIAL_LOGE(MODULE_SERVICE, "%{public}s event handler init fail", __func__);
        return false;
    }

    samgr_ = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (samgr_ == nullptr) {
        SPATIAL_LOGE(MODULE_SERVICE, "Get SystemAbilityManager Failed");
        return false;
    }

    InitLibALgo();
    std::thread comThread(&SpatialaWarenessMgrService::InitCommunication, this);
    std::thread dmThread(&SpatialaWarenessMgrService::CheckDeviceManager, this);
    comThread.join();
    dmThread.join();
    SPATIAL_LOGI(MODULE_SERVICE, "SpatialaWarenessMgrService Init Success!");
    return true;
}

bool SpatialaWarenessMgrService::InitCommunication()
{
    SPATIAL_LOGI(MODULE_SERVICE, "SpatialaWarenessMgrService Init communicator!");
    int time = 1000;
    if (communicator_ == nullptr) {
        communicator_ = std::make_shared<SpatialCommunicator>();
        communicator_->Init(CommunicateFactory::CreateAdapter(mock_));
        if (coordinateSystem_ == nullptr) {
            coordinateSystem_ = std::make_shared<SpatialCoordinateSystem>();
            SPATIAL_LOGI(MODULE_SERVICE, "SpatialaWarenessMgrService Init coordinate system!");
            coordinateSystem_->Init();
        }
    }

    if (!InitSpatialInner()) {
        SPATIAL_LOGE(MODULE_SERVICE, "%{public}s inner init fail", __func__);
        return false;
    }

    sem.Wait();
    DeviceInfo device = communicator_->GetLocalDeviceInfo();
    SPATIAL_LOGI(MODULE_SERVICE, "LocalDevice.Id:%{public}s, LocalDevice.type:%{public}d",
        device.deviceId.c_str(), device.typeId);
    coordinateSystem_->InsertLocalDeviceInfo(device);
    std::this_thread::sleep_for(std::chrono::milliseconds(time));
    communicator_->CreateSessionServer();
    epoch_ = std::make_shared<SpatialMasterEpoch>(handler_, communicator_, coordinateSystem_);
    if (epoch_ != nullptr) {
        SPATIAL_LOGI(MODULE_SERVICE, "master epoch start");
        epoch_->Start();
    }
    SPATIAL_LOGI(MODULE_SERVICE, "SpatialaWarenessMgrService Init communicator success!");
    return true;
}

void SpatialaWarenessMgrService::GetAllLibInst()
{
    auto direction = algorithmHandle_->GetAlgoInstance(AlgorithmType::DIRECTION);
    std::shared_ptr<MsdpAlgorithmInterface> directionAlgo(direction);
    algorithmMap_.insert(std::make_pair(AlgorithmType::DIRECTION, directionAlgo));

    auto distance = algorithmHandle_->GetAlgoInstance(AlgorithmType::DISTANCE);
    std::shared_ptr<MsdpAlgorithmInterface> distanceAlgo(distance);
    algorithmMap_.insert(std::make_pair(AlgorithmType::DISTANCE, distanceAlgo));

    auto angle = algorithmHandle_->GetAlgoInstance(AlgorithmType::ANGLE);
    std::shared_ptr<MsdpAlgorithmInterface> angleAlgo(angle);
    algorithmMap_.insert(std::make_pair(AlgorithmType::DIRECTION, angleAlgo));

    auto io = algorithmHandle_->GetAlgoInstance(AlgorithmType::INNEROUTER);
    std::shared_ptr<MsdpAlgorithmInterface> ioAlgo(io);
    algorithmMap_.insert(std::make_pair(AlgorithmType::INNEROUTER, ioAlgo));

    auto exist = algorithmHandle_->GetAlgoInstance(AlgorithmType::EXIST);
    std::shared_ptr<MsdpAlgorithmInterface> existAlgo(exist);
    algorithmMap_.insert(std::make_pair(AlgorithmType::EXIST, existAlgo));

    auto common = algorithmHandle_->GetAlgoInstance(AlgorithmType::COMMON);
    std::shared_ptr<MsdpAlgorithmInterface> commonAlgo(common);
    algorithmMap_.insert(std::make_pair(AlgorithmType::COMMON, commonAlgo));
}

bool SpatialaWarenessMgrService::InitEventHandler()
{
    if (!eventRunner_) {
        eventRunner_ = AppExecFwk::EventRunner::Create(SPATIAL_AWARENESS_SERVICE_NAME);
        if (eventRunner_ == nullptr) {
            SPATIAL_LOGE(MODULE_SERVICE, "Init failed due to create EventRunner");
            return false;
        }
    }
    if (!handler_) {
        handler_ = std::make_shared<SpatialMgrEventHandler>(eventRunner_, swms);
        if (handler_ == nullptr) {
            SPATIAL_LOGE(MODULE_SERVICE, "Init failed due to create handler error");
            return false;
        }
    }
    return true;
}

void SpatialaWarenessMgrService::InitLibALgo()
{
    if (algorithmHandle_ == nullptr) {
        algorithmHandle_ = std::make_shared<SpatialAlgorithmHandle>();
        if (algorithmHandle_->Init()) {
            SPATIAL_LOGE(MODULE_SERVICE, "%{public}s SpatialAlgorithmHandle init success", __func__);
        }
    }
    algorithmHandle_->LoadAllLib();
    algorithmHandle_->InstallAlllib();
    GetAllLibInst();
}

bool SpatialaWarenessMgrService::InitSpatialInner()
{
    if (!inner_) {
        inner_ = std::make_shared<SpatialMgrServiceInner>();
        if (!inner_->Init()) {
            SPATIAL_LOGE(MODULE_SERVICE, "Init failed due to create inner error");
            return false;
        }
        coordinateSystem_->RegisterUpdateListener(inner_);
    }
    return true;
}

void SpatialaWarenessMgrService::CheckDeviceManager()
{
    int delayTime = 1000;
    auto object = samgr_->CheckSystemAbility(DISTRIBUTED_HARDWARE_DEVICEMANAGER_SA_ID);
    if (object == nullptr) {
        SPATIAL_LOGE(MODULE_SERVICE, "Check SystemAbilityManager Failed");
        std::this_thread::sleep_for(std::chrono::milliseconds(delayTime));
        CheckDeviceManager();
    } else {
        InitSpatialDeviceManager();
    }
}

void SpatialaWarenessMgrService::InitSpatialDeviceManager()
{
    if (deviceMgr_ == nullptr) {
        deviceMgr_ = std::make_shared<SpatialDeviceManager>();
    }

    if (!deviceMgr_->Init()) {
        SPATIAL_LOGE(MODULE_SERVICE,
            "Init failed due to create device manager error");
        return;
    }
    sem.Signal();
}

void SpatialaWarenessMgrService::HandleLoadEvent(int32_t event)
{
    SPATIAL_LOGI(MODULE_SERVICE,
        "HandleEvent: %{public}d", event);
    HandleEvent(event);
}

void SpatialaWarenessMgrService::HandleEvent(int32_t event)
{
    SPATIAL_LOGI(MODULE_SERVICE, "HandleEvent: %{public}d", event);
    if (epoch_ == nullptr) {
        SPATIAL_LOGE(MODULE_SERVICE, "epoch is nullptr");
        return;
    }

    if (epoch_->HandleEvent(event, 0)) {
        return;
    }

    switch (event) {
        case EVENT_DEVICE_INFO_UPDATE: {
            SPATIAL_LOGI(MODULE_SERVICE, "tracking update device info");
            UpdateDeviceInfo();
            break;
        }
        case SpatialAwarenessEpoch::EVENT_EPOCH_MASTER_START: {
            break;
        }
        case SpatialAwarenessEpoch::EVENT_EPOCH_MASTER_END: {
            epoch_ = std::make_shared<SpatialSlaveEpoch>(handler_, communicator_, coordinateSystem_);
            epoch_->Start();
            break;
        }
        case SpatialAwarenessEpoch::EVENT_EPOCH_MASTER_DEMOTE: {
            std::shared_ptr<SpatialMasterEpoch> master = std::static_pointer_cast<SpatialMasterEpoch>(epoch_);
            if (master == nullptr) {
                SPATIAL_LOGE(MODULE_SERVICE, "No master to demote");
                break;
            }
            epoch_ = std::make_shared<SpatialSlaveEpoch>(master);
            epoch_->Start();
            break;
        }
        case SpatialAwarenessEpoch::EVENT_EPOCH_SLAVE_START: {
            break;
        }
        case SpatialAwarenessEpoch::EVENT_EPOCH_SLAVE_END: {
            epoch_ = std::make_shared<SpatialMasterEpoch>(handler_, communicator_, coordinateSystem_);
            epoch_->Start();
            break;
        }
        case SpatialAwarenessEpoch::EVENT_EPOCH_MASTER_AGAIN: {
            epoch_ = std::make_shared<SpatialMasterEpoch>(handler_, communicator_, coordinateSystem_);
            epoch_->Start();
            break;
        }
        default:
            break;
    }
}

float SpatialaWarenessMgrService::GetDistance(std::string deviceId)
{
    bool success = false;
    DeviceInfo device;
    device.deviceId = deviceId;
    std::shared_ptr<DeviceCoordinate> coordinate =
        coordinateSystem_->GetCoordinate(device, success);
    if (!success) {
        return 0;
    }
    return coordinate->distance;
}

float SpatialaWarenessMgrService::GetAngle(std::string deviceId)
{
    bool success = false;
    DeviceInfo device;
    device.deviceId = deviceId;
    std::shared_ptr<DeviceCoordinate> coordinate =
        coordinateSystem_->GetCoordinate(device, success);
    if (!success) {
        return -1;
    }
    return coordinate->angle;
}

uint32_t SpatialaWarenessMgrService::GetDirection(std::string deviceId)
{
    bool success = false;
    DeviceInfo device;
    device.deviceId = deviceId;
    std::shared_ptr<DeviceCoordinate> coordinate =
        coordinateSystem_->GetCoordinate(device, success);
    if (!success) {
        return SpatialDirection::INVALID;
    }
    return coordinate->direction;
}

void SpatialaWarenessMgrService::SubscribeSpatialLocationRelationship(const SpatialLocationMode& mode,
    const SpatialDeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
{
    SPATIAL_LOGI(MODULE_SERVICE,
        "SpatialaWarenessMgrService::SubscribeSpatialLocationRelationship Enter");
    auto uid = IPCSkeleton::GetCallingUid();
    SPATIAL_LOGI(MODULE_SERVICE,
        "%{public}s Request uid, %{public}d", __func__, uid);
    inner_->SubscribeSpatialLocationRelationship(mode, info, callback);
    if (!monitoring_) {
        StartMonitor();
    }
    return;
}

void SpatialaWarenessMgrService::UnSubscribeSpatialLocationRelationship(const SpatialLocationMode& mode,
    const SpatialDeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
{
    SPATIAL_LOGI(MODULE_SERVICE,
        "SpatialaWarenessMgrService::UnSubscribeSpatialLocationRelationship Enter");
    auto uid = IPCSkeleton::GetCallingUid();
    SPATIAL_LOGI(MODULE_SERVICE,
        "%{public}s Request uid, %{public}d", __func__, uid);

    inner_->UnSubscribeSpatialLocationRelationship(mode, info, callback);
    StopMonitor();
    return;
}

void SpatialaWarenessMgrService::StartMonitor()
{
    SPATIAL_LOGI(MODULE_SERVICE, "tracking update device info");
    monitoring_ = true;
    SendEvent(EVENT_DEVICE_INFO_UPDATE, 0, DEVICE_INFO_UPDATE_TIME);
}

void SpatialaWarenessMgrService::StopMonitor()
{
    monitoring_ = false;
    RemoveEvent(EVENT_DEVICE_INFO_UPDATE);
}

void SpatialaWarenessMgrService::SendEvent(int32_t event, int64_t param, int64_t delayTime)
{
    handler_->SendEvent(event, param, delayTime);
}

void SpatialaWarenessMgrService::RemoveEvent(int32_t event)
{
    handler_->RemoveEvent(event);
}

void SpatialaWarenessMgrService::UpdateDeviceInfo()
{
    SPATIAL_LOGI(MODULE_SERVICE, "UpdateDeviceInfo enter");
    std::vector<DeviceInfo> devices;
    if (inner_->GetMonitoringDevices(devices)) {
        coordinateSystem_->UpdateDevices(devices, false);
    }

    if (monitoring_) {
        SendEvent(EVENT_DEVICE_INFO_UPDATE, 0, DEVICE_INFO_UPDATE_TIME);
    }
}

int32_t SpatialaWarenessMgrService::Dump(int32_t fd, const std::vector<std::u16string> &args)
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s begin", __func__);
    SpatialDump &spatialDump = SpatialDump::GetInstance();
    if ((args.empty()) || (args[0].size() != MAX_DMUP_PARAM)) {
        SPATIAL_LOGE(MODULE_SERVICE, "%{public}s param cannot be empty or the length is not 2", __func__);
        dprintf(fd, "cmd param number is not equal to 2\n");
        spatialDump.DumpHelp(fd);
        return SPATIALMANAGER_FAILED;
    }
    bool helpRet = spatialDump.DumpSpatiaHelp(fd, args);
    bool mapRet = spatialDump.DumpSpatialMap(fd, args);
    bool total = helpRet + mapRet;
    if (!total) {
        dprintf(fd, "cmd param is error\n");
        spatialDump.DumpHelp(fd);
        return SPATIALMANAGER_FAILED;
    }
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s end", __func__);
    return SPATIALMANAGER_OK;
}
} // namespace Msdp
} // namespace OHOS